package moriarty.gwt.server;

import java.util.ArrayList;
import java.util.List;

import moriarty.gwt.client.SotdService;
import moriarty.gwt.server.persistence.DataStoreManager;
import moriarty.gwt.server.persistence.Subscriber;
import moriarty.gwt.shared.DataStoreObject;
import moriarty.gwt.shared.Group;
import moriarty.gwt.shared.Submission;
import moriarty.gwt.shared.User;
import moriarty.gwt.shared.Utils;

import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.appengine.api.xmpp.JID;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.googlecode.objectify.Query;


/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class SotdServiceImpl extends RemoteServiceServlet implements
		SotdService {
	
	public SotdServiceImpl(){
	}
	
	/**
	 * RPC Function using the GAE User Service to snag the currently logged in person's email.
	 * This works well since we require everyone to log into their Google account before
	 * using SoTDBot
	 * @return email address
	 */
	public String getCurrentUserEmail(){
		UserService userService = UserServiceFactory.getUserService();
		String currentUser = userService.getCurrentUser().getEmail();
		return currentUser;
	}
	
	/**
	 * RPC Function using the GAE User Service to see if the user is an admin of our Google App Engine project
	 * This may be used to in the future to hide certain "admin" content
	 * @return email address
	 */
	public boolean isCurrentUserAdmin(){
		UserService userService = UserServiceFactory.getUserService();
		return userService.isUserAdmin();
	}
	
	
	/**
	 * RPC Function to send the SoTD submission Instant Messages out to subscribers using XMPP
	 * @param msg - SoTD choice
	 * @return - Comma separated list of users that the sender failed to send to 
	 */
	public String sotdServer(String input) throws IllegalArgumentException {
		String escapedInput = Utils.escapeHtml(input);
		String reply = sendMessage(escapedInput);
		return reply;
	}	
	
	private String sendMessage(String msg){
		
		//Retrieve the latest email list from the DataStore
		Subscriber all = getAllSubscriber();
		String[] subscribers = null;
		
		//Catch the case where there are no subscribers, and return a useful string
		if(all == null){
			return "All email addresses"; //This ends up saying something like, "I was unable to send to: All email addresses"
		}
		else{
			
			//Split the email addresses by comma
			subscribers = all.email.split(",");
			
			//Catch the case when the box has no comma (probably meaning only one email address)
			if(subscribers.length == 0 && msg != null){
				subscribers = new String[]{all.email};
			}	

			//Keep track of the email addresses that fail
			//TODO: Implement functionality to resend to these addresses
			String failedEmails = new String();
			
			//Try to send an instant message to each subscriber IN PARALLEL, keep a list of the failed attempts
			List<String> failedEmailList = new ArrayList<String>();
			for(int i = 0; i < subscribers.length; i++){
				JID jid = new JID(subscribers[i]);
				XMPPSender sender = new XMPPSender(jid,msg);
				String result = sender.send();
				failedEmailList.add(result);
			}
			
			//Create a failed email string (space delimited) to send back to the GUI
			for (String email : failedEmailList) {
					failedEmails += email + " ";
			}
			return failedEmails.trim();
		}
	}

	/**
	 * RPC Function to update our datastore copy of the email subscribers list
	 */
	public void updateEmails(String newEmailList) {
		String escapedNewEmailList = Utils.escapeHtml(newEmailList);
		Subscriber all = getAllSubscriber();
		if(all != null){
			if(!all.email.equals(escapedNewEmailList)){
				all.email = escapedNewEmailList;
				DataStoreManager.put(all);
			}
		}
		else{
			all = new Subscriber("all", escapedNewEmailList);
			DataStoreManager.put(all);
		}
	}
	
	/**
	 * RPC Function to get the current list of users from the data store, calls 
	 * the getAllSubscriber function and just returns the comma delimited string of email addresses.  
	 */
	public String getSubscriberList(){
		Subscriber s = getAllSubscriber();
		if(s == null){
			return "";
		}
		else{
			return s.email;
		}
	}
	
	/**
	 * RPC Function to store the submission using our DataStoreManager
	 */
	public void storeSubmission(Submission s){
		s.escapeSubmission();  //Escapes cross-site scripting chars
		DataStoreManager.put(s);
	}
	
	/**
	 * RPC Function to store the submission using our DataStoreManager
	 */
	public List<Submission> retrieveSubmissions(){
		Query<Submission> response = DataStoreManager.queryClass(Submission.class);
		List<Submission> submissionList = response.list();
		return submissionList;
	}
	
	/**
	 * Makes use of our DataStoreManager to call a query
	 * @return
	 */
	private Subscriber getAllSubscriber(){
		Query<Subscriber> s = DataStoreManager.queryClass(Subscriber.class).filter("name", "all");		
		return s.get();
	}

	/**
	 * RPC Function to store Groups
	 */
	public Group storeGroup(Group g){
		g.escapeGroup(); //Escapes cross-site scripting chars
		DataStoreManager.put(g);
		Query<Group> gg = DataStoreManager.queryClass(Group.class).filter("groupName", g.groupName);
		if( gg.get() != null ){
			System.out.println("Added Group "+ g.groupName + " and generated an id of " + gg.get().id);
			return gg.get();
		}
		else{
			System.out.println("Unable to add Group "+ g.groupName + ". The fucking Data Store returned null when we checked if it stored correctly. Returned original object");
			return g;
		}
	}
	
	/**
	 * RPC Function to retrieve all groups
	 */
	public List<Group> retreiveAllGroups(){
		Query<Group> response = DataStoreManager.queryClass(Group.class);
		List<Group> groupList = response.list();
		return groupList;
	}
	
	/**
	 * RPC Function to retrive all users
	 */
	public List<User> retrieveAllUsers(){
		Query<User> response = DataStoreManager.queryClass(User.class);
		List<User> userList = response.list();
		return userList;
	}
	
	/**
	 * RPC Function to test if user is a member of SoTD
	 */
	public Boolean isMember(String email){
		Query<User> u = DataStoreManager.queryClass(User.class).filter("gmailEmail", email);
		int count = u.count();
		if(count != 0){
			return true;
		}
		else{
			return false;
		}
	}
	
	/**
	 * RPC Function to store a new user
	 */
	public void storeUser(User u){
		DataStoreManager.put(u);
		Query<User> qu = DataStoreManager.queryClass(User.class).filter("gmailEmail", u.gmailEmail);
		System.out.println("Added user " + u.gmailEmail + " to the DataStore, with ID" + qu.get().id);
	}

	/**
	 * RPC Function to grab a REAL SoTD user object from our datastore.
	 */
	public User getCurrentUser(String gmailEmail){
		Query<User> u = DataStoreManager.queryClass(User.class).filter("gmailEmail", gmailEmail);
		return u.get();
	}
	
	/**
	 * RPC Function to update Users in the database. Corrects a problem with duplicate entries.
	 */
	public void updateDataStoreObject(DataStoreObject dso){
		DataStoreManager.update(dso.getClass(), dso);
	}
}
